package com.aneliya.view;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.EventObject;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.event.EventListenerList;
import javax.swing.text.JTextComponent;

import com.aneliya.validator.ValidatorListener;
import com.aneliya.validator.component.IdenticalTextComponentValidator;
import com.aneliya.validator.component.TextComponentValidator;
import com.aneliya.validator.data.EmptyStringValidator;
import com.aneliya.validator.suite.ValidationItem;
import com.aneliya.validator.suite.ValidationSuite;

public class LoginDialog extends JDialog {
	private static final long serialVersionUID = 1L;
	
	private static final String LOGIN_STATE = "loginState";
	private static final String REGISTER_STATE = "registerState";
	
	private static final int LOGIN_DIALOG_WIDTH = 400;
	private static final int LOGIN_DIALOG_HEIGHT = 172;
	private static final int REGISTER_DIALOG_WIDTH = 400;
	private static final int REGISTER_DIALOG_HEIGHT = 225;
	
	private static final int INPUTS_LEFT_MARGIN = 110;
	private static final int INPUTS_HEIGHT = 22;

	private static final String LOGIN_DIALOG_TITLE = "Login";
	private static final String REGISTER_DIALOG_TITLE = "Register";

	private static final String USERNAME_CAPTION = "Username: ";
	private static final String USERNAME_ERROR_MESSAGE = "Please, specify a valid username!";

	private static final String PASSWORD_CAPTION = "Password: ";
	private static final String PASSWORD_ERROR_MESSAGE = "Please, specify a valid password!";
	
	private static final String CONFIRM_PASSWORD_CAPTION = "Confirm Password: ";
	private static final String CONFIRM_PASSWORD_ERROR_MESSAGE = "Please, specify a valid password!";

	private static final String REGISTER_BUTTON_CAPTION = "Register";
	private static final String LOGIN_BUTTON_CAPTION = "Login";
	private static final String CANCEL_BUTTON_CAPTION = "Cancel";

	private static final String PASSWORDS_NOT_IDENTICAL_ERROR_MESSAGE = "Passwords should be identical!";

	protected JLabel usernameLabel;
	protected JTextField usernameTextField;
	protected JLabel usernameErrorLabel;

	protected JPasswordField passwordField;
	protected JLabel passwordLabel;
	protected JLabel passwordErrorLabel;
	
	protected JLabel confirmPasswordLabel;
	protected JPasswordField confirmPasswordField;
	protected JLabel confirmPasswordErrorLabel;

	protected JButton registerButton;
	protected JButton loginButton;

	protected JPanel usernameFormItem;
	protected JPanel passwordFormItem;
	protected JPanel confirmPasswordFormItem;
	protected JPanel buttonsPane;
	
	protected ValidationSuite validationSuite;
	
	protected String state;

	protected String getState() {
		return state;
	}
	protected void setState(String value) throws Exception {
		if (value != state) {
			state = value;
			initializeDialogState();
		}
	}
	
	public String getUsername() {
		return usernameTextField.getText();
	}
	
	public String getPassword() {
		return new String(passwordField.getPassword());
	}
	
	private ValidationItem confirmPasswordValidationItem;
	private ValidationItem identicalPasswordsValidationItem;
	
	private EventListenerList loginDialogListeners;
	
	public LoginDialog(JFrame owner) throws Exception {
		super(owner, LOGIN_DIALOG_TITLE, true);
		initializeGUI();
		initializeValidators();
		initializeDialogState();
		initializeLoginDialogListeners();
		initializeWindowAdapterListener();
		
		loginDialogListeners = new EventListenerList();
	}
	
	protected void initializeGUI() {
		usernameFormItem = createUsernameFormItem();
		passwordFormItem = createUsernamePasswordFormItem();
		confirmPasswordFormItem = createConfirmPasswordFormItem();
		buttonsPane = createButtons();
	}
	
	protected void initializeDialogState() throws Exception {
		if (state == null) {
			setInitialLoginState();
		} else if (state == LOGIN_STATE) {
			setLoginState();
		} else if (state == REGISTER_STATE) {
			setRegisterState();
		}
	}
	
	protected void initializeLoginDialogListeners() {
		MouseListener loginButtonMouseListener = new MouseListener() {
			public void mouseClicked(MouseEvent e) {
				if (state == REGISTER_STATE) {
					try {
						setState(LOGIN_STATE);
					} catch (Exception exception) {
						exception.printStackTrace();
					}
				} else if (state == LOGIN_STATE) {
					dispatchLoginEvent();
				}
			}

			public void mouseEntered(MouseEvent e) {}
			public void mouseExited(MouseEvent e) {}
			public void mousePressed(MouseEvent e) {}
			public void mouseReleased(MouseEvent e) {}
		};
		loginButton.addMouseListener(loginButtonMouseListener);
		
		MouseListener registerButtonMouseListener = new MouseListener() {
			public void mouseClicked(MouseEvent e) {
				if (state == LOGIN_STATE) {
					try {
						setState(REGISTER_STATE);
					} catch (Exception exception) {
						exception.printStackTrace();
					}
				} else if (state == REGISTER_STATE) {
					dispatchRegisterEvent();
				}
			}

			public void mouseEntered(MouseEvent e) {}
			public void mouseExited(MouseEvent e) {}
			public void mousePressed(MouseEvent e) {}
			public void mouseReleased(MouseEvent e) {}
		};
		registerButton.addMouseListener(registerButtonMouseListener);
		
		KeyListener keyListener = new KeyListener() {
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					try {
						if (e.getSource() == loginButton) {
							if (state == REGISTER_STATE) {
								setState(LOGIN_STATE);
							} else if (state == LOGIN_STATE) {
								dispatchLoginEvent();
							}
						} else if (e.getSource() == registerButton) {
							if (state == LOGIN_STATE) {
								setState(REGISTER_STATE);
							} else if (state == REGISTER_STATE) {
								dispatchRegisterEvent();
							}
						} else {
							if (state == LOGIN_STATE && validationSuite.isValid()) {
								dispatchLoginEvent();
							} else if (state == REGISTER_STATE && validationSuite.isValid()) {
								dispatchRegisterEvent();
							}
						}
					} catch (Exception ex) {
						ex.printStackTrace();
					}
				}
			}
			
			public void keyReleased(KeyEvent e) {}
			public void keyTyped(KeyEvent e) {}
		};	
		loginButton.addKeyListener(keyListener);
		registerButton.addKeyListener(keyListener);
		usernameTextField.addKeyListener(keyListener);
		passwordField.addKeyListener(keyListener);
		confirmPasswordField.addKeyListener(keyListener);
	}
	
	private void initializeWindowAdapterListener() {
		WindowAdapter listener = new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				dispatchDialogCloseEvent();
			}
		};
		addWindowListener(listener);
	}
	
	public void addLoginDialogListener(LoginDialogListener listener) {
		loginDialogListeners.add(LoginDialogListener.class, listener);
	}
	
	public void removeLoginDialogListener(LoginDialogListener listener) {
		loginDialogListeners.remove(LoginDialogListener.class, listener);
	}
	
	protected void dispatchLoginEvent() {
		LoginDialogListener[] list = loginDialogListeners.getListeners(LoginDialogListener.class);
	    for (LoginDialogListener listener : list) {
	    	EventObject event = new EventObject(this);
			listener.userLogged(event);
		}
	}
	
	protected void dispatchRegisterEvent() {
		LoginDialogListener[] list = loginDialogListeners.getListeners(LoginDialogListener.class);
	    for (LoginDialogListener listener : list) {
	    	EventObject event = new EventObject(this);
			listener.userRegistered(event);
		}
	}
	
	protected void dispatchDialogCloseEvent() {
		LoginDialogListener[] list = loginDialogListeners.getListeners(LoginDialogListener.class);
	    for (LoginDialogListener listener : list) {
	    	EventObject event = new EventObject(this);
			listener.dialogClosed(event);
		}
	}
	
	protected void initializeValidators() throws Exception {
		TextComponentValidator usernameValidator = new TextComponentValidator(
				usernameTextField, new EmptyStringValidator());
		ValidationItem usernameValidationItem = new ValidationItem(
				usernameValidator, usernameErrorLabel, USERNAME_ERROR_MESSAGE);
		
		TextComponentValidator passwordValidator = new TextComponentValidator(
				passwordField, new EmptyStringValidator());
		ValidationItem passwordValidationItem = new ValidationItem(
				passwordValidator, passwordErrorLabel, PASSWORD_ERROR_MESSAGE);
		
		TextComponentValidator confirmPasswordValidator = new TextComponentValidator(
				confirmPasswordField, new EmptyStringValidator());
		confirmPasswordValidationItem = new ValidationItem(
				confirmPasswordValidator, confirmPasswordErrorLabel, CONFIRM_PASSWORD_ERROR_MESSAGE);
		IdenticalTextComponentValidator identicalPasswordsValidator = new IdenticalTextComponentValidator(
				passwordField, confirmPasswordField);
		identicalPasswordsValidationItem = new ValidationItem(
				identicalPasswordsValidator, confirmPasswordErrorLabel, PASSWORDS_NOT_IDENTICAL_ERROR_MESSAGE);
		
		validationSuite = new ValidationSuite();
		validationSuite.addValidationItem(usernameValidationItem);
		validationSuite.addValidationItem(passwordValidationItem);
		
		ValidatorListener listener = new ValidatorListener() {
			public void validatorChanged(EventObject e) {
				validatorChangedHandler(e);
			}
		};
		
		validationSuite.addValidatorListener(listener);
	}
	
	protected void validatorChangedHandler(EventObject e) {
		if (state == LOGIN_STATE) {
			if (validationSuite.isValid()) {
				loginButton.setEnabled(true);
			} else {
				loginButton.setEnabled(false);
			}
		} else if (state == REGISTER_STATE) {
			if (validationSuite.isValid()) {
				registerButton.setEnabled(true);
			} else {
				registerButton.setEnabled(false);
			}
		}
	}
	
	protected void setInitialLoginState() {
		state = LOGIN_STATE;
		setSize(LOGIN_DIALOG_WIDTH, LOGIN_DIALOG_HEIGHT);
		
		loginButton.setEnabled(false);
		
		Container contentPane = getContentPane();
		contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
		contentPane.add(Box.createRigidArea(new Dimension(10, 10)));
		contentPane.add(usernameFormItem);
		contentPane.add(passwordFormItem);
		contentPane.add(buttonsPane);
	}
	
	protected void setLoginState() throws Exception {
		validationSuite.removeValidationItem(confirmPasswordValidationItem);
		validationSuite.removeValidationItem(identicalPasswordsValidationItem);
		
		setSize(LOGIN_DIALOG_WIDTH, LOGIN_DIALOG_HEIGHT);
		setTitle(LOGIN_DIALOG_TITLE);
		
		loginButton.setText(LOGIN_BUTTON_CAPTION);
		loginButton.setEnabled(validationSuite.isValid());
		registerButton.setEnabled(true);
		
		Container contentPane = getContentPane();
		contentPane.remove(confirmPasswordFormItem);
		
		contentPane.validate();
		validate();
	}
	
	protected void setRegisterState() throws Exception {
		validationSuite.addValidationItem(confirmPasswordValidationItem);
		validationSuite.addValidationItem(identicalPasswordsValidationItem);
		
		setSize(REGISTER_DIALOG_WIDTH, REGISTER_DIALOG_HEIGHT);
		setTitle(REGISTER_DIALOG_TITLE);
		
		registerButton.setEnabled(validationSuite.isValid());
		loginButton.setText(CANCEL_BUTTON_CAPTION);
		loginButton.setEnabled(true);
		
		Container contentPane = getContentPane();
		contentPane.remove(buttonsPane);
		contentPane.add(confirmPasswordFormItem);
		contentPane.add(buttonsPane);
		
		contentPane.validate();
		validate();
	}
	
	protected JPanel createUsernameFormItem() 
	{
		usernameLabel = new JLabel(USERNAME_CAPTION);
		usernameTextField = new JTextField();
		usernameErrorLabel = new JLabel(" ");
		
		return createFormItem(usernameLabel, usernameTextField, usernameErrorLabel);
	}
	
	protected JPanel createUsernamePasswordFormItem() 
	{
		passwordLabel = new JLabel(PASSWORD_CAPTION);
		passwordField = new JPasswordField();
		passwordErrorLabel = new JLabel(" ");
		
		return createFormItem(passwordLabel, passwordField, passwordErrorLabel);
	}
	
	protected JPanel createConfirmPasswordFormItem() 
	{	
		confirmPasswordLabel = new JLabel(CONFIRM_PASSWORD_CAPTION);
		confirmPasswordField = new JPasswordField();
		confirmPasswordErrorLabel = new JLabel(" ");
		
		return createFormItem(confirmPasswordLabel, confirmPasswordField, confirmPasswordErrorLabel);
	}
	
	protected JPanel createButtons () {
		registerButton = new JButton(REGISTER_BUTTON_CAPTION);
		loginButton = new JButton(LOGIN_BUTTON_CAPTION);
		
		JPanel pane = new JPanel();
        pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
        pane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
        pane.add(Box.createHorizontalGlue());
        pane.add(registerButton);
        pane.add(Box.createRigidArea(new Dimension(10, 0)));
        pane.add(loginButton);
		
		return pane;
	}

	protected JPanel createFormItem(JLabel label, JTextComponent inputField, JLabel errorLabel) {
		formatLabel(label);
		formatTextField(inputField);
		formatErrorLabel(errorLabel);
		
		JPanel labelPane = new JPanel();
		labelPane.setPreferredSize(new Dimension(INPUTS_LEFT_MARGIN, INPUTS_HEIGHT));
		labelPane.setLayout(new BoxLayout(labelPane, BoxLayout.X_AXIS));
		labelPane.add(Box.createHorizontalGlue());
		labelPane.add(label);

		JPanel inputPane = new JPanel();
		inputPane.setLayout(new BoxLayout(inputPane, BoxLayout.X_AXIS));
		inputPane.add(labelPane);
		inputPane.add(Box.createRigidArea(new Dimension(10, 0)));
		inputPane.add(inputField);

		JPanel errorLabelPane = new JPanel();
		errorLabelPane.setLayout(new BoxLayout(errorLabelPane, BoxLayout.X_AXIS));
		errorLabelPane.add(Box.createHorizontalGlue());
		errorLabelPane.add(errorLabel);

		JPanel formItemPane = new JPanel();
		formItemPane.setLayout(new BoxLayout(formItemPane, BoxLayout.Y_AXIS));
		formItemPane.setMaximumSize(new Dimension(400, 100));
		formItemPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
		formItemPane.add(Box.createRigidArea(new Dimension(10, 0)));
		formItemPane.add(inputPane);
		formItemPane.add(errorLabelPane);

		return formItemPane;
	}
	
	protected void formatLabel(JLabel label) {
	}
	
	protected void formatTextField(JTextComponent inputField) {
	}
	
	protected void formatErrorLabel(JLabel label) {
		label.setForeground(Color.RED);
	}

}
